package com.weizhu.swufer.core.grpc;

import com.google.protobuf.ByteString;
import com.weizhu.swufer.core.entity.InvokeParam;
import com.weizhu.swufer.core.entity.RpcBaseInfo;
import com.weizhu.swufer.core.exception.RpcBizException;
import com.weizhu.swufer.core.exception.RpcException;
import com.weizhu.swufer.core.filter.FilterChain;
import com.weizhu.swufer.core.filter.FilterManager;
import com.weizhu.swufer.core.metrics.RpcMetrics;
import com.weizhu.swufer.core.rpc.*;
import com.weizhu.swufer.core.server.ServiceHandler;
import com.weizhu.swufer.core.util.ProtoUtil;
import com.weizhu.swufer.grpc.GrpcCommonServiceGrpc;
import com.weizhu.swufer.grpc.Request;
import com.weizhu.swufer.grpc.Response;
import io.grpc.stub.StreamObserver;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.lang.reflect.InvocationTargetException;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;

public class GrpcCommonServerImpl extends GrpcCommonServiceGrpc.GrpcCommonServiceImplBase {

    private final static Logger logger = LoggerFactory.getLogger(GrpcCommonServerImpl.class);

    private final static RpcMetrics localMetrics = new RpcMetrics(UUID.randomUUID().toString());

    private ConcurrentHashMap<String,RpcBaseInfo> localServiceMap;

    public GrpcCommonServerImpl(ConcurrentHashMap<String, RpcBaseInfo> localServiceMap){
        this.localServiceMap = localServiceMap;
    }

    @Override
    public void callService(Request request, StreamObserver<Response> responseObserver) {
        RpcRequest rpcRequest = ProtoUtil.deserialize(request.getRequest().toByteArray(), RpcRequest.class);
        RpcResponse rpcResponse = new RpcResponse();
        String interfaceName = rpcRequest.getInterfaceName();
        FilterChain filterChain = FilterManager.getInstance().buildProviderFilterChain();
        InvokeParam invokeParam = new InvokeParam();
        invokeParam.setRpcRequest(rpcRequest);
        ServiceHandler serviceHandler = new ServiceHandler(localServiceMap);
        filterChain.addFilter(serviceHandler);
        localMetrics.beginStat();
        try {
            Object result = filterChain.doFilter(invokeParam);
            rpcResponse.success(result);
            sendGrpcResponse(rpcResponse,responseObserver);
            localMetrics.endStat(true);
        }
        catch (RpcBizException e) {
            rpcResponse.bizError("Exception from remote biz service : " + e.getMessage());
            rpcResponse.setData(e.getBizRetry());
            sendGrpcResponse(rpcResponse,responseObserver);
            localMetrics.endStat(true);
        }catch (RpcException e){
            rpcResponse.error(e.getMessage(),e);
            sendGrpcResponse(rpcResponse,responseObserver);
            localMetrics.endStat(false);
        }
        catch (Exception e){
            logger.error("",e);
            rpcResponse.error(e.getMessage(),e);
            sendGrpcResponse(rpcResponse,responseObserver);
            localMetrics.endStat(false);
        }

    }

    private void sendGrpcResponse(RpcResponse rpcResponse, StreamObserver<Response> responseObserver){
        ByteString byteString = ByteString.copyFrom(ProtoUtil.serialize(rpcResponse));
        Response response = Response.newBuilder().setResponse(byteString).build();
        responseObserver.onNext(response);
        responseObserver.onCompleted();
    }

    private RpcBaseInfo getRpcBaseInfo(String name){
        return localServiceMap.get(name);
    }

    private Object getBean(String name){
        RpcBaseInfo rpcBaseInfo = localServiceMap.get(name);
        if (rpcBaseInfo != null){
            return rpcBaseInfo.getTarget();
        }
        return null;
    }


    public static RpcMetrics getLocalMetrics() {
        return localMetrics;
    }
}
